import csv
import random
import math
from collections import Counter
import os
import urllib.request


# 自动下载 Iris 数据集
def download_iris_dataset(filename):
    url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
    if not os.path.exists(filename):
        print(f"Downloading {filename} from UCI ML Repository...")
        urllib.request.urlretrieve(url, filename)
        print("Download complete.")
    else:
        print(f"{filename} already exists.")


# 加载数据集并将其划分为训练集和测试集
def load_dataset(filename, split_ratio):
    download_iris_dataset(filename)  # 确保文件存在
    with open(filename, 'r') as csvfile:
        lines = list(csv.reader(csvfile))
        dataset = []
        for i in range(len(lines)):
            if not lines[i]:  # 跳过空行
                continue
            # 将特征值转换为浮点数，保留类别标签
            record = [float(lines[i][j]) if j < 4 else lines[i][j] for j in range(5)]
            dataset.append(record)

        # 打乱数据集以确保随机性
        random.shuffle(dataset)

        # 根据split_ratio划分训练集和测试集
        split_index = int(len(dataset) * split_ratio)
        training_set = dataset[:split_index]
        test_set = dataset[split_index:]
        return training_set, test_set


# 计算欧氏距离
def euclidean_distance(instance1, instance2, length):
    distance = sum((instance1[x] - instance2[x]) ** 2 for x in range(length))
    return math.sqrt(distance)


# 获取最近的 k 个邻居
def get_neighbors(training_set, test_instance, k):
    distances = []
    length = len(test_instance) - 1
    for train_instance in training_set:
        dist = euclidean_distance(test_instance, train_instance, length)
        distances.append((train_instance, dist))
    distances.sort(key=lambda tup: tup[1])
    neighbors = [distances[i][0] for i in range(k)]
    return neighbors


# 预测类别
def predict_classification(neighbors):
    labels = [neighbor[-1] for neighbor in neighbors]
    prediction = Counter(labels).most_common(1)[0][0]
    return prediction


# 计算准确率
def get_accuracy(test_set, predictions):
    correct = sum(1 for x in range(len(test_set)) if test_set[x][-1] == predictions[x])
    return (correct / float(len(test_set))) * 100.0


# 主函数
if __name__ == '__main__':
    # 设置随机种子以保证结果可重复
    random.seed(1)

    # 加载数据集
    training_set, test_set = load_dataset('iris.data', split_ratio=0.67)

    # 进行预测
    predictions = []
    k = 3
    for test_instance in test_set:
        neighbors = get_neighbors(training_set, test_instance, k)
        result = predict_classification(neighbors)
        predictions.append(result)
        print(f'Predicted={result}, Actual={test_instance[-1]}')

    # 输出准确率
    accuracy = get_accuracy(test_set, predictions)
    print(f'Accuracy: {accuracy:.2f}%')